/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package E_explicito_ComputedChaining10bits;

import arquivos.BaseMatematica;
import java.util.ArrayList;

/**
 *
 * @author rafaelob
 */
public class TabelaHash {

    private int tamanhoTabela;
    private Celula celulas[];

    public TabelaHash(int tamanho) {
        celulas = new Celula[tamanho];
        this.tamanhoTabela = tamanho;
        for (int i = 0; i < tamanho; i++) { //Preenche a tabela com as células de acordo com o tamanho da tabela hash
            celulas[i] = new Celula(i);
        }
    }

    private int getIndex(int chave) { //calcula a função hash associada a essa chave
        return chave % tamanhoTabela;
    }

    private int getIncrement(int chave) {
        return BaseMatematica.Max(1, ((chave / tamanhoTabela) % tamanhoTabela));
    }

    private void atualizaPointAfterPosition(Celula celula, int posicao) { //Atualiza o PseudoPonteiro que apontava para a Celula que foi passado como parâmetro para Null
        int index = getIndex(celula.getRecord().getChave());
        int incremento;
        Celula atual = celulas[index];
        Celula anterior;
        while (atual.getPseudoPonteiro() != -1) {
            incremento = getIncrement(atual.getRecord().getChave());
            index = (index + (incremento * atual.getPseudoPonteiro())) % tamanhoTabela;
            anterior = atual;
            atual = celulas[index];
            if (atual.getPosicao() == posicao) {
                anterior.setPseudoPonteiro(-1);
                break;
            }
        }



    }

    public int insertion(int chave) {
        Record record = new Record(chave);
        int index = getIndex(chave);
        if (celulas[index].insertRecord(record)) {
            return 0; // inserido sem colisão
        } else { //Se houve colisão, vamos tratá-la
            if (celulas[index].getRecord().getChave() % tamanhoTabela == index) { //Caso o registro que está no mesmo index dele tenha o mesmo hash,
                int incremento;
                Celula anterior, atual;
                atual = celulas[index];
                while (atual.getPseudoPonteiro() != -1) { //Busca o último da cadeia
                    anterior = atual;
                    incremento = getIncrement(anterior.getRecord().getChave());
                    index = (index + (incremento * anterior.getPseudoPonteiro())) % tamanhoTabela;
                    atual = celulas[index];
                }


                incremento = getIncrement(atual.getRecord().getChave());
                anterior = atual;
                for (int count = 1; count < tamanhoTabela; count++) {
                    index = (index + incremento) % tamanhoTabela;
                    atual = celulas[index];
                    if (atual.getRecord() == null) {
                        atual.insertRecord(record);
                        anterior.setPseudoPonteiro(count);
                        return 1; //inserido com Colisão

                    }

                }
                return -1;



            } else {//Caso contrário será preciso reinseri-los.
                ArrayList<Record> registros = new ArrayList<Record>();
                int incremento, pseudoPonteiro;
                Record registro;
                Celula atual = celulas[index];
                registros.add(atual.getRecord());
                atualizaPointAfterPosition(atual, atual.getPosicao());
                pseudoPonteiro = atual.getPseudoPonteiro();
                registro = atual.getRecord();
                atual.setRecord(record);
                atual.setPseudoPonteiro(-1);
                while (pseudoPonteiro != -1) {
                    incremento = getIncrement(registro.getChave());
                    index = (index + (incremento * pseudoPonteiro)) % tamanhoTabela;
                    atual = celulas[index];
                    registros.add(atual.getRecord());
                    pseudoPonteiro = atual.getPseudoPonteiro();
                    registro = atual.getRecord();
                    atual.setPseudoPonteiro(-1);
                    atual.setRecord(null);


                }


                for (int x = 0; x < registros.size(); x++) { //Reinserindo os registros
                    insertion(registros.get(x).getChave());
                }
                return 2;


            }
        }

    }

    public Celula getCelula(int posicao) {
        return celulas[posicao];


    }

    public int search(int chave) {
        int index = getIndex(chave);
        int acessos = 1;
        int incremento;
        Celula auxiliar = celulas[index];
        while ((auxiliar.getPseudoPonteiro() != -1) && (!auxiliar.searchRecord(chave))) {
            incremento = getIncrement(auxiliar.getRecord().getChave());
            index = (index + (incremento * auxiliar.getPseudoPonteiro())) % tamanhoTabela;
            auxiliar = celulas[index];
            acessos++;
        }
        return acessos;
    }
}


